﻿using System;
using System.Collections.Generic;
using System.Data;
using System.Data.Common;
using System.Linq;
using System.Linq.Expressions;
using System.Text;

namespace SimpleMapper
{
    /// <summary>
    /// 映射数据对象
    /// </summary>
    public sealed partial class Mapper
    {
        /// <summary>
        /// 将<see cref="DataTable"/>映射为<typeparamref name="TDestination"/>列表
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="table"></param>
        /// <param name="ignoreInheritProperty"></param>
        /// <returns></returns>
        public List<TDestination> To<TDestination>(DataTable table, bool ignoreInheritProperty = true) where TDestination : class
        {
            var result = new List<TDestination>();
            if (table == null || table.Rows.Count == 0) return result;
            var columnsMap = new ColumnsMap(table);
            var convertFunc = GetConvertFunc<DataRow, TDestination>(ignoreInheritProperty);
            foreach (DataRow dataRow in table.Rows)
            {
                var item = convertFunc(dataRow, columnsMap);
                result.Add(item);
            }
            return result;
        }
        /// <summary>
        /// 将<see cref="DataRow"/>映射为<typeparamref name="TDestination"/>对象
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="dataRow"></param>
        /// <param name="ignoreInheritProperty"></param>
        /// <returns></returns>
        public TDestination To<TDestination>(DataRow dataRow, bool ignoreInheritProperty = true) where TDestination : class
        {
            if (dataRow == null) return default;
            var columnsMap = new ColumnsMap(dataRow.Table);
            var convertFunc = GetConvertFunc<DataRow, TDestination>(ignoreInheritProperty);
            var result = convertFunc(dataRow, columnsMap);
            return result;
        }
        /// <summary>
        /// 将<see cref="DbDataReader"/>映射为<typeparamref name="TDestination"/>列表
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="reader"></param>
        /// <param name="ignoreInheritProperty"></param>
        /// <returns></returns>
        public List<TDestination> To<TDestination>(DbDataReader reader, bool ignoreInheritProperty = true) where TDestination : class
        {
            var result = new List<TDestination>();
            if (reader == null) return result;
            var columnsMap = new ColumnsMap(reader);
            var convertFunc = GetConvertFunc<DbDataReader, TDestination>(ignoreInheritProperty);

            while (reader.Read())
            {
                var item = convertFunc(reader, columnsMap);
                result.Add(item);
            }
            return result;
        }
        /// <summary>
        /// 将<see cref="DbDataReader"/>首次读取映射为<typeparamref name="TDestination"/>对象
        /// </summary>
        /// <typeparam name="TDestination"></typeparam>
        /// <param name="reader"></param>
        /// <param name="ignoreInheritProperty"></param>
        /// <returns></returns>
        public TDestination ToFirst<TDestination>(DbDataReader reader, bool ignoreInheritProperty = true) where TDestination : class
        {
            var result = default(TDestination);
            if (reader == null || !reader.Read()) return result;
            var columnsMap = new ColumnsMap(reader);
            var convertFunc = GetConvertFunc<DbDataReader, TDestination>(ignoreInheritProperty);
            result = convertFunc(reader, columnsMap);
            return result;
        }
        /// <summary>
        /// 获取转换方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ignoreInheritProperty"></param>
        /// <returns></returns>
        private static Func<TSource, ColumnsMap, TDestination> GetConvertFunc<TSource, TDestination>(bool ignoreInheritProperty)
            where TSource : class
            where TDestination : class
        {
            var func = GetChangeTypeMethod<TSource, TDestination, Func<TSource, ColumnsMap, TDestination>>(key => BuildIndexableObjectMapFunc<TSource, TDestination>(ignoreInheritProperty), ignoreInheritProperty);
            return func;
        }
        /// <summary>
        /// 构建可索引对象映射方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="ignoreInheritProperty"></param>
        /// <returns></returns>
        private static Func<TSource, ColumnsMap, TDestination> BuildIndexableObjectMapFunc<TSource, TDestination>(bool ignoreInheritProperty)
        {
            // 转换类型
            var type = typeof(TDestination);
            // 获取目标对象类型属性
            var properties = GetProperties(type, ignoreInheritProperty);
            // 转换方法 可索引数据对象
            var dataObject = Expression.Parameter(typeof(TSource), "dataObject");
            // 转换方法 输入数据行列映射参数
            var columnsMap = Expression.Parameter(typeof(ColumnsMap), "columnsMap");

            // 转换方法 返回标签
            var @return = Expression.Label(type);
            // 转换方法 返回目标对象变量
            var result = Expression.Variable(type, "result");
            // 转换方法 返回目标对象变量标签
            var gotoReturnLabel = Expression.Return(@return, result);
            // 转换方法 默认的返回目标对象变量标签
            var gotoReturnDefaultLabel = Expression.Label(@return, result);
            // 转换方法 实例化返回目标对象变量
            var resultAssign = Expression.Assign(result, Expression.New(type));
            // 转换方法 检查columnsMap是否为空
            var checkColumnsMap = Expression.Equal(columnsMap, Expression.Constant(null));
            // 转换方法 检查indexableObject是否为空
            var checkIndexableObject = Expression.Equal(dataObject, Expression.Constant(null));
            // 转换方法 如果检查中某项为空，则跳转换到返回标签
            var nullCheck = Expression.IfThen(Expression.Or(checkIndexableObject, checkColumnsMap), gotoReturnLabel);

            // 方法块语句列表
            var blockExpressionList = new List<Expression>(properties.Length + 4)
            {
                resultAssign,
                nullCheck
            };

            // 源行数据 列索引变量
            var columnIndex = Expression.Variable(typeof(int), "columnIndex");

            // 遍历目标对象属性
            foreach (var propertyInfo in properties)
            {
                // 跳过非可转换类型
                if (!propertyInfo.PropertyType.IsConvertibleType()) continue;
                // 目标对象 属性名称
                var propertyName = Expression.Constant(GetPropertyMapName(propertyInfo));
                // 按列索引取单元格值
                var cellValue = Expression.Call(dataObject, "get_Item", null, columnIndex);
                // 获取类型转换器方法
                var converter = ObjectConverter.GetChangeTypeMethodInfo(propertyInfo.PropertyType);
                // 转换为当前属性类型
                var propertyValue = Expression.Call(converter, cellValue);
                // 取当前属性
                var property = Expression.Property(result, propertyInfo);
                // 给当前属性赋值
                var propertyAssign = Expression.Assign(property, propertyValue);
                // 建立条件检查属性名称是否存在，并获取行索引
                var condition = Expression.Call(columnsMap, "TryGetColumnIndex", null, propertyName, columnIndex);
                // 如果条件存在，则执行给属性赋值语句
                var @if = Expression.IfThen(condition, propertyAssign);
                // 当前属性赋值语句添加到方法块
                blockExpressionList.Add(@if);
            }
            // 默认返回值标签
            blockExpressionList.Add(gotoReturnDefaultLabel);

            var methodBlock = Expression.Block(new[] { result, columnIndex }, blockExpressionList);
            var lambda = Expression.Lambda<Func<TSource, ColumnsMap, TDestination>>(methodBlock, dataObject, columnsMap);
            var func = lambda.Compile();
            return func;
        }
        /// <summary>
        /// 列映射
        /// </summary>
        private class ColumnsMap
        {
            /// <summary>
            /// 此字典的key忽略大小写
            /// </summary>
            private readonly Dictionary<string, int> columnsMap = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
            /// <summary>
            /// 生成<see cref="DataTable"/>的列映射信息
            /// </summary>
            /// <param name="table"></param>
            public ColumnsMap(DataTable table)
            {
                if (table?.Columns != null || table.Columns.Count > 0)
                {
                    var columns = table.Columns;
                    for (var i = 0; i < columns.Count; i++)
                    {
                        var column = columns[i];
                        columnsMap.Add(column.ColumnName, i);
                    }
                }
            }
            /// <summary>
            /// 生成<see cref="DbDataReader"/>的列映射信息
            /// </summary>
            /// <param name="reader"></param>
            public ColumnsMap(DbDataReader reader)
            {
                if (reader != null || reader.FieldCount > 0)
                {
                    for (var i = 0; i < reader.FieldCount; i++)
                    {
                        var columnName = reader.GetName(i);
                        columnsMap.Add(columnName, i);
                    }
                }
            }
            /// <summary>
            /// 
            /// </summary>
            /// <param name="columnName"></param>
            /// <param name="columnIndex"></param>
            /// <returns></returns>
            public bool TryGetColumnIndex(string columnName, out int columnIndex)
            {
                columnIndex = -1;
                if (columnName == null) return false;
                return columnsMap.TryGetValue(columnName, out columnIndex);
            }

        }
    }
}
